package com.admgt.util.ftp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

public class Ftp {

	enum UploadStatus {
		REMOTE_FILE_NOT_UNIQUE, CREATE_DIRECTORY_FAIL, CREATE_DIRECTORY_SUCCESS, FILE_EXIST, REMOTE_EQUAL_LOCAL, REMOTE_BIGGER_LOCAL, UPLOAD_FROM_BREAK_SUCCESS, UPLOAD_FROM_BREAK_FAILED, DELETE_REMOTE_FAILED, UPLOAD_NEW_FILES_SUCCESS, UPLOAD_NEW_FILES_FAILED;
	}

	private FTPClient ftpClient = new FTPClient();

	public FTPClient getFtpClient() {
		return ftpClient;
	}

	public Ftp() {
		ftpClient.addProtocolCommandListener(new PrintCommandListener(
				new PrintWriter(System.out)));
		ftpClient.setControlEncoding("UTF-8");
		FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
		conf.setServerLanguageCode("zh");

	}

	public boolean isConnected() {
		return ftpClient.isConnected();
	}

	public boolean connect(String hostname, int port, String username,
			String password) throws IOException {
		ftpClient.connect(hostname, port);
		if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
			if (ftpClient.login(username, password)) {
				return true;
			}
		}
		disconnect();
		return false;
	}

	public void disconnect() throws IOException {
		if (ftpClient.isConnected()) {
			ftpClient.disconnect();
		}
	}

	/**
	 * download the file from Ftp server to local path.
	 * 
	 * @param ftpServerFileName
	 * @param localPath
	 * @return
	 * @throws IOException
	 */
	public boolean download(String ftpServerFileName, String localPath)
			throws IOException {
		ftpClient.enterLocalPassiveMode();// transfer by passive mode
		ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// transfer date by byte
													// code
		boolean result;
		// get remote file size
		FTPFile[] files = ftpClient.listFiles(ftpServerFileName);
		if (files.length != 1) {
			System.out.println("remote file is not unique!");
			return false;
		}
		long remoteSize = files[0].getSize();
		// start to down load files
		File localFile = new File(localPath);
		if (localFile.exists()) {
			result = retryBrokenDownloads(localFile, ftpServerFileName,
					remoteSize);
		} else {
			result = downloadNewFile(localFile, ftpServerFileName);

		}
		return result;
	}

	private boolean downloadNewFile(File localFile, String remote)
			throws IOException {
		OutputStream out = new FileOutputStream(localFile);
		boolean result = ftpClient.retrieveFile(remote, out);
		out.close();
		return result;
	}

	private boolean retryBrokenDownloads(File localFile, String remote,
			long remoteSize) throws IOException {
		OutputStream out = new FileOutputStream(localFile, true);
		long localSize = localFile.length();
		System.out.println("Local file size is:" + localSize);
		if (localSize >= remoteSize) {
			System.out
					.println("Local file size is "
							+ "equal or bigger than remote file size, download stopped.");
			return false;
		}
		ftpClient.setRestartOffset(localSize);
		boolean result = ftpClient.retrieveFile(remote, out);
		out.close();
		return result;
	}

	public UploadStatus upload(String localPath, String ftpServerFileName)
			throws IOException {
		ftpClient.enterLocalPassiveMode();// transfer by passive mode
		ftpClient.setFileType(FTP.BINARY_FILE_TYPE);// transfer date by byte
													// code
		UploadStatus result = null;

		// 对远程目录的处理
		String remoteFileName = ftpServerFileName;
		if (ftpServerFileName.contains("/")) {
			remoteFileName = ftpServerFileName.substring(ftpServerFileName
					.lastIndexOf("/") + 1);
			// 创建服务器远程目录结构，创建失败直接返回
			if (CreateDirecroty(ftpServerFileName, ftpClient) == UploadStatus.CREATE_DIRECTORY_FAIL) {
				return UploadStatus.CREATE_DIRECTORY_FAIL;
			}
		}

		// get remote file size
		FTPFile[] files = ftpClient.listFiles(ftpServerFileName);
		/*
		 * FTPFile[] files2 = ftpClient.listFiles("/"); for(FTPFile f : files2){
		 * //System.out.println(":"+f.getRawListing());
		 * System.out.println(":"+f.toFormattedString()); }
		 */
		// start to upload files
		File localFile = new File(localPath);
		if (files.length == 1) {
			long remoteSize = files[0].getSize();
			result = retryBrokenUploads(localFile, ftpServerFileName,
					remoteSize);
		} else {
			result = uploadNewFile(localFile, ftpServerFileName);
		}
		return result;
	}

	private UploadStatus CreateDirecroty(String remote, FTPClient ftpClient)
			throws IOException {
		UploadStatus status = UploadStatus.CREATE_DIRECTORY_SUCCESS;
		String directory = remote.substring(0, remote.lastIndexOf("/") + 1);
		if (!directory.equalsIgnoreCase("/")
				&& !ftpClient.changeWorkingDirectory(new String(directory
						.getBytes("GBK"), "iso-8859-1"))) {
			// 如果远程目录不存在，则递归创建远程服务器目录
			int start = 0;
			int end = 0;
			if (directory.startsWith("/")) {
				start = 1;
			} else {
				start = 0;
			}
			end = directory.indexOf("/", start);
			while (true) {
				String subDirectory = new String(remote.substring(start, end)
						.getBytes("GBK"), "iso-8859-1");
				if (!ftpClient.changeWorkingDirectory(subDirectory)) {
					if (ftpClient.makeDirectory(subDirectory)) {
						ftpClient.changeWorkingDirectory(subDirectory);
					} else {
						System.out.println("创建目录失败");
						return UploadStatus.CREATE_DIRECTORY_FAIL;
					}
				}
				start = end + 1;
				end = directory.indexOf("/", start);

				// 检查所有目录是否创建完毕
				if (end <= start) {
					break;
				}
			}
		}
		return status;
	}

	private UploadStatus uploadNewFile(File localFile, String remote)
			throws IOException {
		InputStream in = new FileInputStream(localFile);
		UploadStatus result = null;
		if (ftpClient.storeFile(remote, in)) {
			result = UploadStatus.UPLOAD_NEW_FILES_SUCCESS;
		} else {
			result = UploadStatus.UPLOAD_NEW_FILES_FAILED;
		}
		in.close();
		return result;
	}

	private UploadStatus retryBrokenUploads(File localFile, String remote,
			long remoteSize) throws IOException {
		UploadStatus result = null;
		long localSize = localFile.length();
		System.out.println("Local file size is:" + localSize);
		if (remoteSize == localSize) {
			System.out.println("Remote file size is "
					+ "equal local file size, download stopped.");
			return UploadStatus.REMOTE_EQUAL_LOCAL;
		} else if (remoteSize > localSize) {
			System.out.println("Remote file size is "
					+ "bigger than local file size, download stopped.");
			return UploadStatus.REMOTE_BIGGER_LOCAL;
		}
		InputStream in = new FileInputStream(localFile);
		if (in.skip(remoteSize) == remoteSize) {
			ftpClient.setRestartOffset(remoteSize);
			if (ftpClient.storeFile(remote, in)) {
				result = UploadStatus.UPLOAD_FROM_BREAK_SUCCESS;
			} else {
				result = UploadStatus.UPLOAD_FROM_BREAK_FAILED;
			}
		}
		in.close();
		return result;
	}

	public static void main(String[] args) {
		Ftp ftp = new Ftp();
		try {
			// download files
			/*
			 * ftp.connect("10.225.106.40", 21, "myusername", "12345678");
			 * SimpleDateFormat format = new
			 * SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); Long start =
			 * System.currentTimeMillis();
			 * System.out.println("Start="+format.format(new Date(start)));
			 * ftp.download("dataRemote.txt", "C:/守望幸福.txt");
			 * 
			 * Long end = System.currentTimeMillis();
			 * System.out.println("end="+format.format(new Date(end)));
			 * ftp.disconnect();
			 */

			// upload files
			ftp.connect("10.225.106.40", 21, "myusername", "12345678");
			SimpleDateFormat format = new SimpleDateFormat(
					"yyyy-MM-dd hh:mm:ss");
			Long start = System.currentTimeMillis();

			System.out.println(ftp.upload("C:/Squad+7青春偶像剧《守望幸福》(第一季).wmv",
					"守望幸福.wmv"));

			Long end = System.currentTimeMillis();
			System.out.println("Start=" + format.format(new Date(start)));
			System.out.println("end=" + format.format(new Date(end)));
			ftp.disconnect();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}